<template>
  <transition @enter="unfoldEnter"
              @before-enter="unfoldBeforeEnter"
              @before-leave="unfoldBeforeLeave"
              @after-leave="unfoldAfterLeave">
    <slot></slot>
  </transition>
</template>

<script lang="ts">
import { defineComponent, inject, onBeforeUnmount, onMounted, provide, ref } from 'vue'
import { addClassName, removeClassName } from '@/base/utils'
import mitt, { Emitter } from 'mitt'

export default defineComponent({
  name: 'FxTransitionUnfold',
  setup () {
    const unfoldMitt = mitt()
    const currentDom = ref<HTMLElement>()
    const childUnfold = ref<number>(0)

    // 发送小心 自身
    provide<Emitter['emit']>('unfoldMittEmit', unfoldMitt.emit)
    // 接收消息
    const unfoldMittEmit = inject<Emitter['emit'] | undefined>('unfoldMittEmit', undefined)

    const handleChildUnfold = (height: number | undefined): void => {
      if (height && currentDom.value) {
        childUnfold.value = height
        currentDom.value.style.height = `${currentDom.value?.scrollHeight + height}px`
      } else if (!height && currentDom.value) {
        currentDom.value.style.height = `${currentDom.value?.scrollHeight - childUnfold.value}px`
        childUnfold.value = 0
      }
    }

    const unfoldBeforeEnter = (el: HTMLElement) => {
      currentDom.value = el
      addClassName(el, 'fx-transition-unfold')
      el.style.height = '0'
    }
    const unfoldEnter = (el: HTMLElement) => {
      if (el.scrollHeight) {
        el.style.height = `${el.scrollHeight}px`
        if (unfoldMittEmit) unfoldMittEmit('unfoldEmitHeight', el.scrollHeight)
      }
    }
    const unfoldBeforeLeave = (el: HTMLElement) => {
      el.style.height = '0'
      if (unfoldMittEmit) unfoldMittEmit('unfoldEmitHeight', 0)
    }
    const unfoldAfterLeave = (el: HTMLElement) => {
      removeClassName(el, 'fx-transition-unfold')
    }

    onMounted(() => {
      unfoldMitt.on<number | undefined>('unfoldEmitHeight', handleChildUnfold)
    })

    onBeforeUnmount(() => {
      unfoldMitt.off('unfoldEmitHeight', handleChildUnfold)
    })

    return {
      unfoldBeforeEnter,
      unfoldEnter,
      unfoldBeforeLeave,
      unfoldAfterLeave
    }
  }
})
</script>
